﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using ServiceStack.Redis;

namespace Redis.ServiceStack
{
    public class MyRedisDbClient
    {
        private readonly RedisClient _redis;
        private readonly Semaphore _sem;

        public MyRedisDbClient(string host, int port, long db = 0, string password = null)
        {
            _redis = new RedisClient(host, port, password, db);
            _sem = new Semaphore(1, 1);
        }

        #region 对象JSON化

        /// <summary>
        /// 把object对象JSON化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        private string ObjectToJson<T>(T obj)
        {
            return JsonConvert.SerializeObject(obj);
        }

        #endregion

        #region 还原Redis中的JSON化对象

        /// <summary>
        /// 还原Redis中的JSON化对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="json"></param>
        /// <returns></returns>
        private T JsonToObject<T>(string json)
        {
            if (json == null)
                json = ObjectToJson(default(T));
            return JsonConvert.DeserializeObject<T>(json);
        }

        #endregion
        
        public bool KeyRename(string oldRedisKey, string newRedisKey)
        {
            _sem.WaitOne();
            _redis.Rename(oldRedisKey, newRedisKey);
            _sem.Release();
            return true;
        }

        public bool KeyExists(string redisKey)
        {  
            _sem.WaitOne();
            var x = _redis.ContainsKey(redisKey);
            _sem.Release();
            return x;
        }
        
        public void FlushDb()
        {
            _sem.WaitOne();
            _redis.FlushDb();
            _sem.Release();
        }

        /// <summary>
        /// key模糊搜索
        /// </summary>
        /// <param name="key"></param>
        /// <param name="leftStar">true:需要左匹配；false:需要右匹配</param>
        /// <param name="rightStar"></param>
        /// <returns></returns>
        public List<string> GetAllKeyLike(string key, bool leftStar = false, bool rightStar = false)
        {
            var left = leftStar ? "*" : "";
            var right = rightStar ? "*" : "";
            _sem.WaitOne();
            var listKey = _redis.SearchKeys(left + key + right);
            _sem.Release();
            return listKey;
        }

        public string Type(string redisKey)
        {
            _sem.WaitOne();
            var type = _redis.Type(redisKey);
            _sem.Release();
            return type;
        }

        #region String

        public bool StringAddOrUpdate<T>(string redisKey, T value)
        {
            var jsonValue = ObjectToJson(value);
            _sem.WaitOne();
            _redis.SetEntry(redisKey, jsonValue);
            _sem.Release();
            return true;
        }

        public bool StringAddOrUpdate<T>(string redisKey, T value, TimeSpan liveTime)
        {
            var jsonValue = ObjectToJson(value);
            _sem.WaitOne();
            _redis.SetEntry(redisKey, jsonValue, liveTime);
            _sem.Release();
            return true;
        }

        public bool StringAddIfNotExists<T>(string redisKey, T value)
        {
            var jsonValue = ObjectToJson(value);
            _sem.WaitOne();
            var x = _redis.SetEntryIfNotExists(redisKey, jsonValue);
            _sem.Release();
            return x;
        }

        public bool StringUpdateIfExists<T>(string redisKey, T value)
        {
            _sem.WaitOne();
            var isExists = _redis.ContainsKey(redisKey);
            if (!isExists)
            {

            }
            else
            {
                var jsonValue = ObjectToJson(value);
                _redis.SetEntry(redisKey, jsonValue);
            }
            _sem.Release();
            return isExists;
        }

        public string StringGet(string redisKey)
        {
            _sem.WaitOne();
            var jsonValue = _redis.GetValue(redisKey);
            _sem.Release();
            return jsonValue;
        }

        public T StringGet<T>(string redisKey)
        {
            var jsonValue = StringGet(redisKey);
            var result = JsonToObject<T>(jsonValue);
            return result;
        }

        public bool StringDelete(string redisKey)
        {
            _sem.WaitOne();
            var x = _redis.Remove(redisKey);
            _sem.Release();
            return x;
        }


        public long StringIncrement(string redisKey, int value = 1)
        {
            _sem.WaitOne();
            var x = _redis.IncrementValueBy(redisKey, value);
            _sem.Release();
            return x;
        }

        public long StringDecrement(string redisKey, int value = 1)
        {
            _sem.WaitOne();
            var x = _redis.DecrementValueBy(redisKey, value);
            _sem.Release();
            return x;
        }

        //public double StringIncrement(string redisKey, double value)
        //{
        //    _sem.WaitOne();
        //    var x = _redis.IncrementValueBy(redisKey, value);
        //    _sem.Release();
        //    return x;
        //}

        //public double StringDecrement(string redisKey, double value)
        //{
        //    _sem.WaitOne();
        //    var x = _redis.IncrementValueBy(redisKey, -value);
        //    _sem.Release();
        //    return x;
        //}

        #endregion

        #region List

        public long ListRightPush<T>(string redisKey, T value)
        {
            var jsonValue = ObjectToJson(value);
            _sem.WaitOne();
            _redis.PushItemToList(redisKey, jsonValue);
            var x = _redis.GetListCount(redisKey);
            _sem.Release();
            return x;
        }

        public T ListRightPop<T>(string redisKey)
        {
            _sem.WaitOne();
            var jsonValue = _redis.PopItemFromList(redisKey);
            _sem.Release();
            var result = JsonToObject<T>(jsonValue);
            return result;
        }

        public long ListLeftPush<T>(string redisKey, T value)
        {
            var jsonValue = ObjectToJson(value);
            _sem.WaitOne();
            _redis.EnqueueItemOnList(redisKey, jsonValue);
            var x = _redis.GetListCount(redisKey);
            _sem.Release();
            return x;
        }

        public T ListLeftPop<T>(string redisKey)
        {
            _sem.WaitOne();
            var jsonValue = _redis.RemoveStartFromList(redisKey);
            _sem.Release();
            var result = JsonToObject<T>(jsonValue);
            return result;
        }

        public long ListRemove<T>(string redisKey, T value)
        {
            var jsonValue = ObjectToJson(value);
            _sem.WaitOne();
            var x = _redis.RemoveItemFromList(redisKey, jsonValue);
            _sem.Release();
            return x;
        }

        public long ListLength(string redisKey)
        {
            _sem.WaitOne();
            var x = _redis.GetListCount(redisKey);
            _sem.Release();
            return x;
        }

        public bool ListDelete(string redisKey)
        {
            _sem.WaitOne();
            var x = _redis.Remove(redisKey);
            _sem.Release();
            return x;
        }

        #endregion

        #region Hash

        public bool HashSet<T>(string redisKey, string hashKey, T value)
        {
            var jsonValue = ObjectToJson(value);
            _sem.WaitOne();
            var x = _redis.SetEntryInHash(redisKey, hashKey, jsonValue);
            _sem.Release();
            return x;
        }

        public bool HashSetIfNotExists<T>(string redisKey, string hashKey, T value)
        {
            var jsonValue = ObjectToJson(value);
            _sem.WaitOne();
            var x = _redis.SetEntryInHashIfNotExists(redisKey, hashKey, jsonValue);
            _sem.Release();
            return x;
        }

        public ConcurrentDictionary<string, T> HashGet<T>(string redisKey)
        {
            var result = new ConcurrentDictionary<string, T>();
            _sem.WaitOne();
            var dic = _redis.GetAllEntriesFromHash(redisKey);
            _sem.Release();
            foreach (var kv in dic)
            {
                var key = kv.Key;
                var jsonValue = kv.Value;
                var value = JsonToObject<T>(jsonValue);
                result.TryAdd(key, value);
            }
            return result;
        }

        public T HashGet<T>(string redisKey, string hashKey)
        {
            _sem.WaitOne();
            var jsonValue = _redis.GetValueFromHash(redisKey, hashKey);
            _sem.Release();
            var result = JsonToObject<T>(jsonValue);
            return result;
        }

        public long HashLength(string redisKey)
        {
            _sem.WaitOne();
            var x = _redis.GetHashCount(redisKey);
            _sem.Release();
            return x;
        }

        public bool HashExists(string redisKey, string hashKey)
        {
            _sem.WaitOne();
            var x = _redis.HashContainsEntry(redisKey, hashKey);
            _sem.Release();
            return x;
        }

        public bool HashDelete(string redisKey, string hashKey)
        {
            _sem.WaitOne();
            var x = _redis.RemoveEntryFromHash(redisKey, hashKey);
            _sem.Release();
            return x;
        }

        public bool HashDelete(string redisKey)
        {
            _sem.WaitOne();
            var x = _redis.Remove(redisKey);
            _sem.Release();
            return x;
        }

        public long HashIncrement(string redisKey, string hashKey, long value = 1)
        {
            _sem.WaitOne();
            var x = _redis.IncrementValueInHash(redisKey, hashKey, value);
            _sem.Release();
            return x;
        }

        public long HashDecrement(string redisKey, string hashKey, long value = 1)
        {
            _sem.WaitOne();
            var x = _redis.IncrementValueInHash(redisKey, hashKey, -value);
            _sem.Release();
            return x;
        }

        //public double HashIncrement(string redisKey, string hashKey, double value)
        //{
        //    var x = _redis.IncrementValueInHash(redisKey, hashKey, value);
        //    return x;
        //}

        //public double HashDecrement(string redisKey, string hashKey, double value)
        //{
        //    var x = _redis.IncrementValueInHash(redisKey, hashKey, -value);
        //    return x;
        //}

        #endregion

        #region 全局锁
        
        public long LockKey(string redisKey)
        {
            _sem.WaitOne();
            var x = _redis.SetNX(redisKey, new byte[0]);
            _sem.Release();
            return x;
        }

        public bool UnlockKey(string redisKey)
        {
            _sem.WaitOne();
            var x = _redis.Remove(redisKey);
            _sem.Release();
            return x;
        }

        #endregion


    }
}
